package com.yilian.io;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

/**
 * Java IO流功能演示类
 * 
 * 这个类展示了Java IO流的各种基本用法和技巧，包括：
 * 1. 字节流的基本操作（FileInputStream、FileOutputStream）
 * 2. 字符流的基本操作（FileReader、FileWriter）
 * 3. 缓冲流的使用（BufferedInputStream、BufferedReader等）
 * 4. 数据流的使用（DataInputStream、DataOutputStream）
 * 5. 对象流的使用（ObjectInputStream、ObjectOutputStream）
 * 6. 转换流的使用（InputStreamReader、OutputStreamWriter）
 * 7. 打印流的使用（PrintStream、PrintWriter）
 * 8. NIO.2的基本操作
 * 
 * 学习要点：
 * - IO流是Java中处理输入输出的基础
 * - 字节流处理二进制数据，字符流处理文本数据
 * - 缓冲流可以提高IO性能
 * - 各种装饰器模式的应用
 * - 资源管理和异常处理的重要性
 * 
 * @author 练习项目
 * @version 1.0
 */
public class IODemo {
    
    // 工作目录，用于存放测试文件
    private static final String WORK_DIR = "io_test_files";
    
    /**
     * 主方法 - 程序入口点
     * 依次演示IO流的各种功能
     * 
     * @param args 命令行参数（未使用）
     */
    public static void main(String[] args) {
        System.out.println("=== Java IO流功能演示 ===\n");
        
        // 创建测试目录
        createTestDirectory();
        
        // 1. 字节流基本操作
        demonstrateByteStreams();
        
        // 2. 字符流基本操作
        demonstrateCharacterStreams();
        
        // 3. 缓冲流使用
        demonstrateBufferedStreams();
        
        // 4. 数据流使用
        demonstrateDataStreams();
        
        // 5. 对象流使用
        demonstrateObjectStreams();
        
        // 6. 转换流使用
        demonstrateConversionStreams();
        
        // 7. 打印流使用
        demonstratePrintStreams();
        
        // 8. NIO.2基本操作
        demonstrateNIO2();
        
        // 9. 文件复制工具
        demonstrateFileCopy();
        
        // 10. 目录遍历
        demonstrateDirectoryTraversal();
        
        System.out.println("\n=== IO流演示完成 ===");
    }
    
    /**
     * 创建测试目录
     * 用于存放各种测试文件
     */
    private static void createTestDirectory() {
        try {
            Path workDir = Paths.get(WORK_DIR);
            if (!Files.exists(workDir)) {
                Files.createDirectories(workDir);
                System.out.println("创建测试目录: " + workDir.toAbsolutePath());
            }
        } catch (IOException e) {
            System.err.println("创建测试目录失败: " + e.getMessage());
        }
    }
    
    /**
     * 演示字节流基本操作
     * 
     * 字节流用于处理二进制数据，包括：
     * - FileInputStream：从文件读取字节
     * - FileOutputStream：向文件写入字节
     * - 适合处理图片、音频、视频等二进制文件
     * 
     * 重要概念：
     * - 字节流按字节读写，不关心数据内容
     * - 需要手动处理字节数组
     * - 适合处理大文件，但性能相对较低
     */
    public static void demonstrateByteStreams() {
        System.out.println("1. 字节流基本操作:");
        System.out.println("----------------------------------------");
        
        String fileName = WORK_DIR + "/byte_test.dat";
        
        try {
            // 写入字节数据
            System.out.println("写入字节数据到文件: " + fileName);
            try (FileOutputStream fos = new FileOutputStream(fileName)) {
                // 写入一些测试数据
                byte[] data = {65, 66, 67, 68, 69, 70}; // ABCDEF的ASCII码
                fos.write(data);
                fos.write(10); // 换行符
                fos.write("Hello Byte Stream!".getBytes(StandardCharsets.UTF_8));
            }
            System.out.println("字节数据写入完成");
            
            // 读取字节数据
            System.out.println("从文件读取字节数据:");
            try (FileInputStream fis = new FileInputStream(fileName)) {
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = fis.read(buffer)) != -1) {
                    System.out.println("读取到 " + bytesRead + " 字节:");
                    // 显示为字符串
                    System.out.println("  字符串形式: " + new String(buffer, 0, bytesRead, StandardCharsets.UTF_8));
                    // 显示为十六进制
                    System.out.print("  十六进制: ");
                    for (int i = 0; i < bytesRead; i++) {
                        System.out.printf("%02X ", buffer[i]);
                    }
                    System.out.println();
                }
            }
            
        } catch (IOException e) {
            System.err.println("字节流操作失败: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println();
    }
    
    /**
     * 演示字符流基本操作
     * 
     * 字符流用于处理文本数据，包括：
     * - FileReader：从文件读取字符
     * - FileWriter：向文件写入字符
     * - 自动处理字符编码
     * - 适合处理文本文件
     * 
     * 重要概念：
     * - 字符流按字符读写，自动处理编码
     * - 比字节流更适合处理文本
     * - 性能相对较好，但内存占用较高
     */
    public static void demonstrateCharacterStreams() {
        System.out.println("2. 字符流基本操作:");
        System.out.println("----------------------------------------");
        
        String fileName = WORK_DIR + "/char_test.txt";
        
        try {
            // 写入字符数据
            System.out.println("写入字符数据到文件: " + fileName);
            try (FileWriter fw = new FileWriter(fileName, StandardCharsets.UTF_8)) {
                fw.write("这是第一行文本\n");
                fw.write("这是第二行文本\n");
                fw.write("这是第三行文本\n");
                fw.write("包含中文和English混合内容\n");
                fw.write("数字: 12345\n");
                fw.write("特殊字符: !@#$%^&*()\n");
            }
            System.out.println("字符数据写入完成");
            
            // 读取字符数据
            System.out.println("从文件读取字符数据:");
            try (FileReader fr = new FileReader(fileName, StandardCharsets.UTF_8)) {
                char[] buffer = new char[1024];
                int charsRead;
                while ((charsRead = fr.read(buffer)) != -1) {
                    System.out.println("读取到 " + charsRead + " 字符:");
                    System.out.println("内容:");
                    System.out.println(new String(buffer, 0, charsRead));
                }
            }
            
        } catch (IOException e) {
            System.err.println("字符流操作失败: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println();
    }
    
    /**
     * 演示缓冲流使用
     * 
     * 缓冲流是装饰器模式的典型应用，包括：
     * - BufferedInputStream：缓冲字节输入流
     * - BufferedOutputStream：缓冲字节输出流
     * - BufferedReader：缓冲字符输入流
     * - BufferedWriter：缓冲字符输出流
     * 
     * 重要概念：
     * - 缓冲流可以提高IO性能
     * - 支持按行读取文本
     - 减少系统调用次数
     */
    public static void demonstrateBufferedStreams() {
        System.out.println("3. 缓冲流使用:");
        System.out.println("----------------------------------------");
        
        String fileName = WORK_DIR + "/buffered_test.txt";
        
        try {
            // 使用缓冲流写入数据
            System.out.println("使用缓冲流写入数据到文件: " + fileName);
            try (BufferedWriter bw = new BufferedWriter(new FileWriter(fileName, StandardCharsets.UTF_8))) {
                bw.write("第一行：这是缓冲流测试\n");
                bw.write("第二行：缓冲流可以提高性能\n");
                bw.write("第三行：支持按行写入\n");
                bw.write("第四行：适合处理大量文本\n");
                bw.write("第五行：测试完成\n");
            }
            System.out.println("缓冲流写入完成");
            
            // 使用缓冲流读取数据（按行读取）
            System.out.println("使用缓冲流按行读取数据:");
            try (BufferedReader br = new BufferedReader(new FileReader(fileName, StandardCharsets.UTF_8))) {
                String line;
                int lineNumber = 1;
                while ((line = br.readLine()) != null) {
                    System.out.println("第" + lineNumber + "行: " + line);
                    lineNumber++;
                }
            }
            
            // 使用缓冲字节流复制文件
            System.out.println("使用缓冲字节流复制文件:");
            String copyFileName = WORK_DIR + "/buffered_test_copy.txt";
            try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileName));
                 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(copyFileName))) {
                
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = bis.read(buffer)) != -1) {
                    bos.write(buffer, 0, bytesRead);
                }
                System.out.println("文件复制完成: " + copyFileName);
            }
            
        } catch (IOException e) {
            System.err.println("缓冲流操作失败: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println();
    }
    
    /**
     * 演示数据流使用
     * 
     * 数据流用于读写基本数据类型，包括：
     * - DataInputStream：读取基本数据类型
     * - DataOutputStream：写入基本数据类型
     * - 支持Java的所有基本数据类型
     * - 数据格式与平台无关
     * 
     * 重要概念：
     * - 数据流保持数据的类型信息
     * - 适合存储结构化数据
     * - 数据格式与平台无关
     */
    public static void demonstrateDataStreams() {
        System.out.println("4. 数据流使用:");
        System.out.println("----------------------------------------");
        
        String fileName = WORK_DIR + "/data_test.dat";
        
        try {
            // 使用数据流写入基本数据类型
            System.out.println("使用数据流写入基本数据类型到文件: " + fileName);
            try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(fileName))) {
                dos.writeBoolean(true);
                dos.writeByte(100);
                dos.writeChar('A');
                dos.writeShort(1000);
                dos.writeInt(100000);
                dos.writeLong(1000000000L);
                dos.writeFloat(3.14f);
                dos.writeDouble(3.14159265359);
                dos.writeUTF("Hello Data Stream!");
                dos.writeUTF("数据流测试");
            }
            System.out.println("数据流写入完成");
            
            // 使用数据流读取基本数据类型
            System.out.println("使用数据流读取基本数据类型:");
            try (DataInputStream dis = new DataInputStream(new FileInputStream(fileName))) {
                System.out.println("Boolean: " + dis.readBoolean());
                System.out.println("Byte: " + dis.readByte());
                System.out.println("Char: " + dis.readChar());
                System.out.println("Short: " + dis.readShort());
                System.out.println("Int: " + dis.readInt());
                System.out.println("Long: " + dis.readLong());
                System.out.println("Float: " + dis.readFloat());
                System.out.println("Double: " + dis.readDouble());
                System.out.println("String1: " + dis.readUTF());
                System.out.println("String2: " + dis.readUTF());
            }
            
        } catch (IOException e) {
            System.err.println("数据流操作失败: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println();
    }
    
    /**
     * 演示对象流使用
     * 
     * 对象流用于序列化和反序列化对象，包括：
     * - ObjectInputStream：反序列化对象
     * - ObjectOutputStream：序列化对象
     * - 对象必须实现Serializable接口
     * - 支持对象图的序列化
     * 
     * 重要概念：
     * - 序列化将对象转换为字节序列
     * - 反序列化将字节序列恢复为对象
     * - 对象必须实现Serializable接口
     * - 支持版本兼容性
     */
    public static void demonstrateObjectStreams() {
        System.out.println("5. 对象流使用:");
        System.out.println("----------------------------------------");
        
        String fileName = WORK_DIR + "/object_test.dat";
        
        try {
            // 创建学生对象
            List<Student> students = new ArrayList<>();
            students.add(new Student("2021001", "张三", 20, "计算机科学", 
                                   java.time.LocalDate.of(2021, 9, 1), 3.8));
            students.add(new Student("2021002", "李四", 21, "软件工程", 
                                   java.time.LocalDate.of(2021, 9, 1), 3.6));
            students.add(new Student("2021003", "王五", 22, "数据科学", 
                                   java.time.LocalDate.of(2021, 9, 1), 3.9));
            
            // 使用对象流序列化对象
            System.out.println("使用对象流序列化学生对象到文件: " + fileName);
            try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileName))) {
                oos.writeObject(students);
                System.out.println("对象序列化完成，共序列化 " + students.size() + " 个学生对象");
            }
            
            // 使用对象流反序列化对象
            System.out.println("使用对象流反序列化学生对象:");
            try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName))) {
                @SuppressWarnings("unchecked")
                List<Student> loadedStudents = (List<Student>) ois.readObject();
                
                System.out.println("反序列化成功，共加载 " + loadedStudents.size() + " 个学生对象:");
                for (Student student : loadedStudents) {
                    System.out.println("  " + student.getSummary());
                }
            }
            
        } catch (IOException | ClassNotFoundException e) {
            System.err.println("对象流操作失败: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println();
    }
    
    /**
     * 演示转换流使用
     * 
     * 转换流用于在字节流和字符流之间转换，包括：
     * - InputStreamReader：字节流转换为字符流
     * - OutputStreamWriter：字符流转换为字节流
     * - 可以指定字符编码
     * - 解决编码问题
     * 
     * 重要概念：
     * - 转换流是字节流和字符流的桥梁
     * - 可以指定字符编码
     * - 解决跨平台编码问题
     */
    public static void demonstrateConversionStreams() {
        System.out.println("6. 转换流使用:");
        System.out.println("----------------------------------------");
        
        String fileName = WORK_DIR + "/conversion_test.txt";
        
        try {
            // 使用转换流写入数据（指定UTF-8编码）
            System.out.println("使用转换流写入数据到文件: " + fileName);
            try (OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(fileName), StandardCharsets.UTF_8)) {
                osw.write("这是转换流测试\n");
                osw.write("支持指定字符编码\n");
                osw.write("解决编码问题\n");
                osw.write("包含中文和English\n");
                osw.write("特殊字符: 中文标点，。！？\n");
            }
            System.out.println("转换流写入完成");
            
            // 使用转换流读取数据（指定UTF-8编码）
            System.out.println("使用转换流读取数据:");
            try (InputStreamReader isr = new InputStreamReader(new FileInputStream(fileName), StandardCharsets.UTF_8)) {
                char[] buffer = new char[1024];
                int charsRead;
                while ((charsRead = isr.read(buffer)) != -1) {
                    System.out.println("读取到 " + charsRead + " 字符:");
                    System.out.println(new String(buffer, 0, charsRead));
                }
            }
            
        } catch (IOException e) {
            System.err.println("转换流操作失败: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println();
    }
    
    /**
     * 演示打印流使用
     * 
     * 打印流提供了方便的打印方法，包括：
     * - PrintStream：字节打印流
     * - PrintWriter：字符打印流
     * - 支持各种数据类型的打印
     * - 不会抛出IOException
     * 
     * 重要概念：
     * - 打印流提供了方便的打印方法
     * - 不会抛出IOException
     * - 支持格式化输出
     */
    public static void demonstratePrintStreams() {
        System.out.println("7. 打印流使用:");
        System.out.println("----------------------------------------");
        
        String fileName = WORK_DIR + "/print_test.txt";
        
        try {
            // 使用打印流写入数据
            System.out.println("使用打印流写入数据到文件: " + fileName);
            try (PrintWriter pw = new PrintWriter(new FileWriter(fileName, StandardCharsets.UTF_8))) {
                pw.println("这是打印流测试");
                pw.println("支持各种数据类型:");
                pw.println("字符串: " + "Hello World");
                pw.println("整数: " + 100);
                pw.println("浮点数: " + 3.14);
                pw.println("布尔值: " + true);
                pw.println("字符: " + 'A');
                
                // 格式化输出
                pw.printf("格式化输出: %s, %d, %.2f\n", "测试", 123, 45.678);
                pw.printf("当前时间: %tF %tT\n", System.currentTimeMillis(), System.currentTimeMillis());
            }
            System.out.println("打印流写入完成");
            
            // 读取并显示文件内容
            System.out.println("文件内容:");
            try (BufferedReader br = new BufferedReader(new FileReader(fileName, StandardCharsets.UTF_8))) {
                String line;
                while ((line = br.readLine()) != null) {
                    System.out.println("  " + line);
                }
            }
            
        } catch (IOException e) {
            System.err.println("打印流操作失败: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println();
    }
    
    /**
     * 演示NIO.2基本操作
     * 
     * NIO.2提供了更现代的文件操作API，包括：
     * - Files类：文件操作工具类
     * - Path接口：路径表示
     * - 支持异步IO操作
     * - 更好的性能
     * 
     * 重要概念：
     * - NIO.2是Java 7引入的新IO API
     * - 提供了更现代的文件操作方式
     * - 支持异步IO和更好的性能
     */
    public static void demonstrateNIO2() {
        System.out.println("8. NIO.2基本操作:");
        System.out.println("----------------------------------------");
        
        try {
            // 创建目录
            Path dirPath = Paths.get(WORK_DIR, "nio2_test");
            if (!Files.exists(dirPath)) {
                Files.createDirectories(dirPath);
                System.out.println("创建目录: " + dirPath);
            }
            
            // 创建文件
            Path filePath = dirPath.resolve("nio2_test.txt");
            if (!Files.exists(filePath)) {
                Files.createFile(filePath);
                System.out.println("创建文件: " + filePath);
            }
            
            // 写入文件
            List<String> lines = List.of(
                "这是NIO.2测试文件",
                "使用Files类操作文件",
                "支持批量写入和读取",
                "性能更好，API更现代"
            );
            Files.write(filePath, lines, StandardCharsets.UTF_8);
            System.out.println("文件写入完成");
            
            // 读取文件
            System.out.println("文件内容:");
            List<String> readLines = Files.readAllLines(filePath, StandardCharsets.UTF_8);
            for (String line : readLines) {
                System.out.println("  " + line);
            }
            
            // 文件属性
            System.out.println("文件属性:");
            System.out.println("  大小: " + Files.size(filePath) + " 字节");
            System.out.println("  创建时间: " + Files.getAttribute(filePath, "creationTime"));
            System.out.println("  最后修改时间: " + Files.getLastModifiedTime(filePath));
            System.out.println("  是否可读: " + Files.isReadable(filePath));
            System.out.println("  是否可写: " + Files.isWritable(filePath));
            
        } catch (IOException e) {
            System.err.println("NIO.2操作失败: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println();
    }
    
    /**
     * 演示文件复制工具
     * 
     * 使用不同的IO流实现文件复制，比较性能差异
     */
    public static void demonstrateFileCopy() {
        System.out.println("9. 文件复制工具:");
        System.out.println("----------------------------------------");
        
        String sourceFile = WORK_DIR + "/char_test.txt";
        String targetFile1 = WORK_DIR + "/copy1.txt";
        String targetFile2 = WORK_DIR + "/copy2.txt";
        
        try {
            // 方法1：使用字节流复制
            System.out.println("方法1：使用字节流复制");
            long startTime = System.currentTimeMillis();
            copyFileWithByteStreams(sourceFile, targetFile1);
            long endTime = System.currentTimeMillis();
            System.out.println("字节流复制完成，耗时: " + (endTime - startTime) + "ms");
            
            // 方法2：使用缓冲流复制
            System.out.println("方法2：使用缓冲流复制");
            startTime = System.currentTimeMillis();
            copyFileWithBufferedStreams(sourceFile, targetFile2);
            endTime = System.currentTimeMillis();
            System.out.println("缓冲流复制完成，耗时: " + (endTime - startTime) + "ms");
            
            // 方法3：使用NIO.2复制
            System.out.println("方法3：使用NIO.2复制");
            startTime = System.currentTimeMillis();
            Files.copy(Paths.get(sourceFile), Paths.get(WORK_DIR + "/copy3.txt"));
            endTime = System.currentTimeMillis();
            System.out.println("NIO.2复制完成，耗时: " + (endTime - startTime) + "ms");
            
        } catch (IOException e) {
            System.err.println("文件复制失败: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println();
    }
    
    /**
     * 使用字节流复制文件
     */
    private static void copyFileWithByteStreams(String source, String target) throws IOException {
        try (FileInputStream fis = new FileInputStream(source);
             FileOutputStream fos = new FileOutputStream(target)) {
            
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }
        }
    }
    
    /**
     * 使用缓冲流复制文件
     */
    private static void copyFileWithBufferedStreams(String source, String target) throws IOException {
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source));
             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(target))) {
            
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, bytesRead);
            }
        }
    }
    
    /**
     * 演示目录遍历
     * 
     * 使用NIO.2的Files.walk()方法遍历目录结构
     */
    public static void demonstrateDirectoryTraversal() {
        System.out.println("10. 目录遍历:");
        System.out.println("----------------------------------------");
        
        try {
            Path workDir = Paths.get(WORK_DIR);
            System.out.println("遍历目录: " + workDir.toAbsolutePath());
            
            // 使用Files.walk()遍历目录
            Files.walk(workDir)
                 .filter(Files::isRegularFile)
                 .forEach(path -> {
                     try {
                         System.out.println("文件: " + path.getFileName() + 
                                          " (大小: " + Files.size(path) + " 字节)");
                     } catch (IOException e) {
                         System.err.println("获取文件大小失败: " + e.getMessage());
                     }
                 });
            
        } catch (IOException e) {
            System.err.println("目录遍历失败: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println();
    }
} 